import * as THREE from 'three';
import {
  AmbientLight,
  AxesHelper,
  BufferGeometry,
  Clock,
  DirectionalLight,
  DoubleSide,
  Geometry,
  Material,
  MathUtils,
  Mesh,
  PlaneBufferGeometry,
  Points,
  PointsMaterial,
  SphereBufferGeometry,
  Texture,
  Vector2,
  Vector3,
} from 'three';
import BaseThreeApp from '../BaseThreeApp';
import DebugInfo from '../DebugInfo';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';

const worldCenter = new Vector3(0, 0, 0);

class _15LayerTest extends BaseThreeApp {
  public scene: THREE.Scene;
  public camera: THREE.PerspectiveCamera;
  public renderer: THREE.WebGLRenderer;
  public controller: OrbitControls;
  public geos: (Geometry | BufferGeometry)[] = [];
  public mats: Material[] = [];
  public texs: Texture[] = [];
  public clock: Clock;
  public mousePositon: Vector2 = new Vector2();
  public ambient: AmbientLight;

  start() {
    this.scene = new THREE.Scene();
    const cw = this.clientWidth / 2;
    const ch = this.clientHeight / 2;
    this.camera = new THREE.PerspectiveCamera(60, cw / ch, 0.1, 10000);
    this.resetCamera();
    this.scene.add(this.camera);

    const material = new THREE.MeshLambertMaterial({ color: 0x00ffff });
    this.mats.push(material);
    const buf = new SphereBufferGeometry(0.1);
    this.geos.push(buf);
    this.scene.add(new Mesh(buf, material));
    const axisHelp = new AxesHelper(5);
    this.geos.push(axisHelp.geometry);
    this.scene.add(axisHelp);
    this.controller = new OrbitControls(this.camera, this.element);
    this.controller.target.copy(worldCenter);
    this.controller.update();

    this.renderer = new THREE.WebGLRenderer({
      canvas: this.element,
    });
    this.renderer.setDrawingBufferSize(this.clientWidth, this.clientHeight, 1);

    const ambient = new AmbientLight(0xffffff, 0.2);
    this.camera.add(ambient);
    this.ambient = ambient;

    const planeBuf = new PlaneBufferGeometry(2000, 2000, 250, 250);
    this.geos.push(planeBuf);
    const matPlane = new PointsMaterial({ color: 0x313333, size: 0.1 });
    const plane = new Points(planeBuf, matPlane);
    plane.rotation.x = MathUtils.degToRad(90);
    this.scene.add(plane);

    const colors = [0xff0000, 0x00ff00, 0x0000ff];
    const geometry = new THREE.BoxBufferGeometry(20, 20, 20);

    for (let i = 0; i < 300; i++) {
      const layer = i % 3;

      const object = new THREE.Mesh(
        geometry,
        new THREE.MeshLambertMaterial({ color: colors[layer] })
      );

      object.position.x = Math.random() * 800 - 400;
      object.position.y = Math.random() * 800 - 400;
      object.position.z = Math.random() * 800 - 400;

      object.rotation.x = Math.random() * 2 * Math.PI;
      object.rotation.y = Math.random() * 2 * Math.PI;
      object.rotation.z = Math.random() * 2 * Math.PI;

      object.scale.x = Math.random() + 0.5;
      object.scale.y = Math.random() + 0.5;
      object.scale.z = Math.random() + 0.5;

      object.layers.set(layer);

      this.scene.add(object);
    }

    for (let i = 0; i < 3; i++) {
      this.camera.layers.enable(i);
      this.ambient.layers.enable(i);
    }

    this.clock = new Clock(true);
    this.isRunning = true;
    requestAnimationFrame(this.render.bind(this));
  }

  public render(time: number) {
    if (!this.isRunning || this.element === null) {
      return;
    }
    DebugInfo.Instance.begin();
    this.renderer.clear(true, true, true);
    this.renderer.render(this.scene, this.camera);
    DebugInfo.Instance.end();
    requestAnimationFrame(this.render.bind(this));
  }

  public onmousemove(e: MouseEvent) {
    this.mousePositon.x = (e.offsetX / this.clientWidth) * 2 - 1;
    this.mousePositon.y = -(e.offsetY / this.clientHeight) * 2 + 1;
  }

  onresize(e: UIEvent) {
    super.onresize(e);
    console.log('onresive');
    if (this.renderer) {
      this.renderer.setDrawingBufferSize(
        this.clientWidth,
        this.clientHeight,
        1
      );
      if (this.camera) {
        const aspect = this.clientWidth / this.clientHeight;
        this.camera.aspect = aspect;
        this.camera.updateProjectionMatrix();

        const cw = this.clientWidth / 2;
        const ch = this.clientHeight / 2;
      }
    }
  }

  protected resetCamera() {
    this.camera.position.set(500, 500, 500);
    this.camera.lookAt(worldCenter);
  }

  public onkeydown(e: KeyboardEvent): any | void {
    if (e.key === 'r' || e.key === ' ') {
      this.resetCamera();
    } else if (e.key === '1') {
      this.camera.layers.toggle(0);
    } else if (e.key === '2') {
      this.camera.layers.toggle(1);
    } else if (e.key === '3') {
      this.camera.layers.toggle(2);
    } else if (e.key === '4') {
      this.camera.layers.enableAll();
    } else if (e.key === '5') {
      this.camera.layers.disableAll();
    }
  }

  destory() {
    this.geos.forEach((v) => v.dispose());
    this.geos = [];
    this.mats.forEach((v) => v.dispose());
    this.mats = [];
    this.texs.forEach((v) => v.dispose());
    this.texs = [];
    super.destory();
  }
}

export default new _15LayerTest();
